\documentclass[11pt,a4paper]{article}
\usepackage[dvips]{graphicx}
\usepackage{natbib} 
\begin{document}

\title{Modern Collision Detection and Response Algorithms For
Computer Games: Refinement of the Irrlicht Engine}
\author{Ben Gilbert W0062289}
\date{20 April 2008}
\maketitle
\pagebreak

\section{Introduction}
Refinement of the collision detection and response component of the
Irrlicht engine is the focus of the studies conducted for CSC8480.
In CSC8490 studies had been conducted into the  general techniques
used for collision detection and response in computer game
applications.  These studies were summarised in three reports~\cite[]{a1}~\cite[]{a2}~\cite[]{a3}, which
included a summary of the techniques used by the Irrlicht Engine. Inefficiencies in this engine were
postulated.  The studies documented in this report focus on refinement of the implementation of the vector mathematics.  It was found that
optimisation during compilation was critical to efficiency.
Expression templates as a means of improving the compilation of the
vector class was also investigated.

\section{Analysis of the Irrlicht Engine}
Irrlicht 1.4 ~\cite[]{irrlicht} was used for all of the work described
below.  The engine was built with the default compilation flags unless
otherwise noted.  The system used to run these tests was comprised of: an  AMD Athlon
64 processor with 1GB of RAM, an NVIDIA 6800GS with 256MB of RAM,
and Linux 2.6.22-14-generic operating system.  The gcc c++ compiler version 4.1.3 was used for
all compilation.

\subsection{Test Program}
\verb+benchmark+ is a program that was written to quantitatively examine
the efficiency of the engine.  The source code to \verb+benchmark+ is shown
in Appendix~\ref{main} and Appendix~\ref{game}. The criteria for the design was: the engine's collision methods would used be as they would in typical game application
but these methods would be called a great number of time such that
improvements in the engines efficiency would have an obvious effect on
execution time or frame rate.  The program
implements one thousand cube scene nodes (an inbuilt class) that possess a fixed
velocity that is determined on initialisation.  These cubes are
placed in a Quake map (``20kdm2.bsp'', a map distributed with the engine) that has been loaded
into an octree mesh.  Each cube was assigned an initial random velocity at
the start of the program's execution.  For each frame, the cubes moved a
small distance in the direction of this velocity.  The program ran in
two modes: with and without collision algorithms being activated for the
cubes.  Collisions detection and response could occur between the cubes and the walls of the
Quake map but not between cubes themselves (they pass through each other).  The program would executed for a finite
number of frames, specified by a command line argument.  The number
of Frames Per Second (FPS) was printed to the standard output.  For
this system it was found that one thousand boxes produced a
significantly slower program with collision detection than without
(50:1 FPS).

\subsection{Profiling the Test Program}
\verb+gprof+, a GNU profiler was used to examine the execution of the test
program. Both the Irrlicht library and the source had been built with
the \verb+gprof+ flag set (\verb+-pg+).  The test program was executed
for 100 frames with and without collision algorithms enabled.
A summary of the results is shown in
Tables~\ref{top_10_called_collision},~\ref{top_10_longest_collision},~\ref{top_10_called},
and~\ref{top_10_longest}.  The total execution time was also obtained.  See Table~\ref{execution_time}. The UNIX command
\verb+time+ was used to obtain the measurements shown in Table~\ref{execution_time}. 

\begin{table}[p]
\caption{Functions with the highest call count. Collision algorithms enabled. 100 frames.}\label{top_10_called_collision}
\begin{tabular}{|p{11cm}|p{2cm}|}
\hline
Function & Called \\
\hline
irr::core::vector3d\verb+<+float\verb+>+::operator= & 666044047 \\
\hline
irr::core::vector3d\verb+<+float\verb+>+::vector3d(float, float, float) & 52920734 \\
\hline
irr::core::vector3d\verb+<+float\verb+>+::dotProduct  & 528843636 \\
\hline
irr::core::CMatrix4\verb+<+float\verb+>+::transformVect( irr::core::vector3d\verb+<+float\verb+>+\verb+&+) const \verb+&+ & 
492305248 \\
\hline
irr::core::vector3d\verb+<+float\verb+>+::operator- & 360752198 \\
\hline
irr::core::array\verb+<+irr::core::triangle3d\verb+<+float\verb+>+, irr::core::irrAllocator\verb+<+irr::core::triangle3d
\verb+<+float\verb+>+ \verb+>+ \verb+>+::operator\verb+[]}+ & 328325155 \\
\hline
irr::core::vector3d\verb+<+float\verb+>+::vector3d() & 167395899 \\
\hline
irr::core::vector3d\verb+<+float\verb+>+::crossProduct & 164787776 \\
\hline
irr::core::vector3d\verb+<+float\verb+>+::normalize & 164098599 \\
\hline
\end{tabular}
\end{table}


\begin{table}[p]
\caption{Functions with the longest execution (total over program
  execution and excluding subroutines).  Collision algorithms enabled.  100 frames.}
\label{top_10_longest_collision}
\begin{tabular}{|p{11cm}|p{2cm}|}
\hline
Function & Total Time [s] \\
\hline 
irr::core::CMatrix4\verb+<+float\verb+>+::transformVect( irr::core::vector3d\verb+<+float\verb+>&+ const & 19.33 \\
\hline 
irr::core::vector3d\verb+<+float\verb+>+::dotProduct & 10.86 \\
\hline 
irr::core::vector3d\verb+<+float\verb+>+::operator= & 7.75\\
\hline 
irr::core::vector3d\verb+<+float\verb+>+::operator- & 6.55\\
\hline 
irr::core::vector3d\verb+<+float\verb+>+::normalize & 5.44\\
\hline 
irr::scene::CSceneCollisionManager::testTriangleIntersection & 4.82\\
\hline 
irr::core::vector3d\verb+<+float\verb+>+::vector3d(float, float, float) & 3.9\\
\hline 
irr::core::vector3d\verb+<+float\verb+>+::crossProduct & 3.71\\
\hline 
irr::scene::COctTreeTriangleSelector::getTrianglesFromOctTree & 3.54\\
\hline 
irr::core::reciprocal\_squareroot & 3.27\\
\hline 
\end{tabular}
\end{table}


\begin{table}[p]
\caption{Functions with the highest call count. Collision algorithms disabled. 100 frames.}\label{top_10_called}
\begin{tabular}{|p{11cm}|p{2cm}|}
\hline
Function & Called \\
\hline
irr::core::CMatrix4\verb+<+float\verb+>+::operator\verb+[]+(unsigned int) const & 6415224 \\
\hline
irr::core::vector3d\verb+<+float\verb+>+::vector3d(float, float, float) & 3591727 \\
\hline
irr::core::array\verb+<+unsigned char, irr::core::irrAllocator\verb+<+unsigned char\verb+>+
\verb+>+::operator\verb+[]+ &  2824605 \\
\hline
irr::video::SColor::getRed & 2721905 \\
\hline
irr::video::SColor::getGreen  & 2721905 \\
\hline
irr::video::SColor::getAlpha  & 2721905 \\
\hline
irr::video::SColor::toOpenGLColor &  2721800 \\
\hline
irr::core::iszero & 2252414 \\
\hline
irr:core::CMatrix4\verb+<+float\verb+>+::operator()(int, int) const & 2125014 \\
\hline
irr::core::vector3d\verb+<+float\verb+>+::operator- & 1740682 \\
\hline
\end{tabular}
\end{table}


\begin{table}[p]
\caption{Functions with the longest execution (total over program
  executing and excluding subroutines).  Collision algorithms disabled.  100 frames.}
\label{top_10_longest}
\begin{tabular}{|p{10cm}|p{2cm}|}
\hline
Function & Total Time [s] \\
\hline
irr::core::CMatrix4\verb+<+float\verb+>+::operator* & 0.08 \\
\hline
irr::core::SColor::toOpenGLColor & 0.06 \\
\hline
irr::core::COpenGLDriver::setTransform & 0.05 \\
\hline
irr::core::vector3d\verb+<+float\verb+>+::operator- & 0.06 \\
\hline
irr::core::CMatrix4\verb+<+float\verb+>+::transformVect( irr::core::vector3d\verb+<+float\verb+>+
const\verb+&+) & 0.05 \\
\hline
irr::core::CMatrix4\verb+<+float\verb+>+::isIdentity & 0.05 \\
\hline
decode\_mcu & 0.05 \\
\hline
irr::video::COpenGLDriver::drawVertexPrimitiveList & 0.04 \\
\hline
irr::core::vector3d\verb+<+float\verb+>+::vector3d(irr::core::vector\verb+<+float\verb+>+ const\verb+&+) & 0.04 \\
\hline
irr::core::vector\verb+<+float\verb+>+::crossProduct & 0.04 \\
\hline
\end{tabular}
\end{table}

\begin{table}[p]
\caption{Total execution time for 100 frames.}\label{execution_time}
\begin{tabular}{|l|l|l|l|}
\hline
Collision Algorithms & Real [s] & User [s] & Sys [s] \\
\hline
Enabled &  149.452 &   148.285 &  0.704 \\
Disabled & 2.697 &   1.152 &  0.096 \\
\hline
\end{tabular}
\end{table}

\section{Refinement of Vector Implementation}
The above results indicate that test program runs significantly slower
with collision detection enabled.  When collision detection is enabled
a significant portion of this time is consumed by the manipulation of
vector objects.  For the reason, refinement of the implementation of
vectors is the focus of this portion of the study.

\subsection{Expression Templates}
The use of expression templates when implementing vector libraries is claimed to
lead to efficiency increases~\cite[]{blinn}.  The concept behind
expression templates is to define templates classes associated with
certain operations (such as dot product, addition, etc), such that the
compiler will create code that uses less temporary variables and makes
better use of registers in evaluation of the operations.  A more
detailed explanation of expression templates is shown in Appendix~\ref{expression_templates}.

To measure the efficiency improvements of expression templates, a
simple vector header file was written, using source code provided
by~\cite[pp.101-103]{blinn}.  This header file implements vector
addition and scalar multiplication only.  The header file is
reproduced in Appendix~\ref{expression_vector}.  Identical test programs
were written to compare the execution speed of the
expression template implementation versus the Irrlicht library
implementation.  The test programs evaluated the four expressions:   
\begin{enumerate}
\item d = a + b; 
\item d = 3 * a + 4 * b;  
\item d = a + 3 * (b + c);   
\item d = 3 * a + 4 * b + 5 * c;
\end{enumerate}
Where a, b, c, d are vector objects.

\verb+vector_test.cpp+, the expression template test program is reproduced in
Appendix~\ref{expression_vector_test}.  \verb+vector_test_irr.cpp+, the Irrlicht test program is
reproduced in Appendix~\ref{irr_vector_test}.  The execution time of each
program was measured  using calls to the POSIX interval timers via \verb+getitimer+ and
\verb+setitimer+.  (This timing method was used as \verb+gprof+ does not provide
complete results when optimisation is used, and the UNIX \verb+time+
command has only millisecond accuracy).  The two programs were compiled with no optimisation, -O2
optimisation  and -O3 optimisation (-O2 and -O3 are gcc compiler
optimisation flags).  See Table
~\ref{execution_time_vectors}.  Note: the several entries give 0\(\mu\)s for
execution time.  Changing the value of \verb+limit+ to a much greater value
(such as \verb+LONG_MAX+) does not create a measurable change in execution
time.


Assembly files of the test programs were also produced.  The number of
lines of the assembly code gives a rough measure of
efficiency.  With -O2 the expression template
implementation had an assembly file with 45 lines.  With -O3
optimisation the Irrlicht implementation had 329 lines.  The assembly
code has been included in Appendices~\ref{expression_assembly} and~\ref{irr_assembly}. 

Applying -O3 optimisation to all of the files in the Irrlicht engine
library and \verb+benchmark+ resulted in the execution times shown in
Table~\ref{optimised_time}.  Note, that the initial velocity of the
cubes appears to be not purely random but biased to a certain
direction.  

\begin{table}
\caption{Execution time for vector test programs}\label{execution_time_vectors}
\begin{tabular}{|p{0.2\textwidth}|p{0.2\textwidth}| p{0.2\textwidth}| p{0.2\textwidth}|}
\hline
Implementation & \multicolumn{3}{|c|}{Time [\(\mu\)s]} \\
\cline{2-4}
 & No optimisation & -O2 & -O3 \\

\hline
Expression Templates & 212013, 440027, 444027, 672042 &   0, 0, 0, 0 & 0, 4001, 0, 0 \\
\hline
Irrlicht & 84005, 184011, 156009, 140009 &   0, 24002, 20002, 40003 &  0, 0, 0, 0 \\
\hline
\end{tabular}
\end{table}

\begin{table}
\caption{benchmark execution time, collision algorithms
  enabled, 100 frames, with no optimisation and -O3 optimisation.}
\label{optimised_time}
\begin{tabular}{|l|l|l|l|}
\hline
Optimisation  & Real [s] & User [s] & Sys [s] \\
\hline
None &  149.452 &   148.285 &  0.704 \\
-O3 & 60.052 & 58.592 & 0.300 \\
\hline
\end{tabular}
\end{table}



The above results indicate that with optimisation a significantly
faster program was achieved.  With no flagged optimisation the
expression templates are slower than a straight forward
implementation.  Without optimisation, simple but powerful
short cuts might be not taken by the compiler.  -O3 optimisation also
leads to one expression template expression to have a greater execution
time than when -O3 optimisation is used.  Although -O3 should lead to
faster execution times than -O2, this is not a guaranteed result~\cite[]{gcc_intro}.  By default, the Irrlicht Engine's
library only optimises the source code that deals with rendering and
loading textures.

The significantly fewer assembly instructions required by the expression
template implementation supports the claim of faster execution times.  A close examination of the assembly code should be
conducted before a definite conclusion can be made.  Such an
examination will not be performed due to time restrictions.  Existing
vector libraries using expression templates could be used as a
substitute for Irrlicht's existing vector class.  An example of such a
library is tvmet~\cite[]{tvmet}.  This also will not be performed
due to time restrictions.

\section{Conclusion}
Significant improvement in the execution time of Irrlicht applications
can be achieved by optimisation during compilation.  This is at least
partially due to the execution time improvement of the vector objects,
which has been shown to be improved considerably with optimisation.
It has also been found that vector mathematics amount to a considerable
portion of the executions time.  Expression templates are a means to achieve an even faster program
with less optimisation.


Implementation of vectors using expression templates would require
considerable work even if an external library such as tvmet~\cite[]{tvmet}
was used.  As such, no further work will be pursued in this area.

The future focus of this study will move to looking at how the engine
can be improved at a system architecture level.  The knowledge gained
in the portion of the study will be
valuable to understand the impact of different architectural designs.


\bibliographystyle{agsm}
\addcontentsline{toc}{chapter}{\bibname}
\bibliography{all}{}

\appendix
\section{Expression Templates}\label{expression_templates}
The evaluation of expressions for objects with defined arithmetic
operators (e.g. +, -, *, /) often involve the creation of several
temporary variables.  The goal of expression templates is to be able
compile these expression into code that minimises the use of temporary
variables and allows for more efficient use of registers.  To use expression templates, template classes are
defined to represent the result of an arithmetic operation.  These template
classes do not actually contain any data.  During compilation, these
classes cause the compiler to expand the arithmetic expressions in an
efficient manner.  Blinn describes this as ``coddling the
compiler''~\cite[p.103]{blinn}.  Expression templates are a case where
the clarity of the source code is traded for efficiency.  

A straight forward implementation of a vector class might appear as
follows (considering only vector addition for clarity):
\begin{verbatim}
class Vector {
  float v[3];
public:
  Vector() {;}
  Vector(float d0,float d1,float d2)
    {v[0]=d0; v[1]=d1; v[2]=d2;}
  float operator[](int i) const
    {return v[i];}
  float& operator[](int i)
    {return v[i];}
\end{verbatim}
etc...
\begin{verbatim}
}

Vector& operator+=(const Vector& V2)
{
   v[0]+=V2[0];
   v[1]+=V2[1];
   v[2]+=V2[2];
}

inline const
Vector operator+(const Vector& A, const Vector& B)
{
   return Vector(A) += B;
}

\end{verbatim}

Now consider the following, where A, B, C, and D are all Vector objects:

A = B + (C + D);

This statement when processed by a compiler will be executed as follows:
\begin{enumerate}
\item Vector temp1 = C + D;
\item Vector temp2 = B + temp1;
\item A = temp2;
\end{enumerate}
In other words, temporary variables are created to store the
result of each binary operation.

Ideally the original statement would be evaluated like this:
\begin{enumerate}
\item A[0] = B[0] + C[0] + D[0];
\item A[1] = B[1] + C[1] + D[1];
\item A[2] = B[2] + C[2] + D[2];
\end{enumerate}
The advantage of the above statements is that temporary variables do
not need to be created and that the addition can also take place using
the machine's registers.

The expression template header file in Appendix~\ref{expression_vector}
defines three classes: a Vector class, a Sum class, a Product class
(product of a vector times a scalar), and an Expression class.  For
simplicity the Product class will be ignored for now.  When the
expression A = B + (C + D) is evaluated the following occurs:

\begin{enumerate}
\item C + D is replaced by a Sum\verb+<+C, D\verb+>+ object.
\item B + ( C+ D ) is replaced by a Sum\verb+<+B, Sum\verb+<+C, D\verb+>+ object.
\item A is initialised by a call to Vector(  Sum\verb+<+B, Sum\verb+<+C, D\verb+>+ ).  This
  function evaluates the argument as follows:
  \begin{enumerate}
    \item A[0] =  Sum\verb+<+B, Sum\verb+<+C, D\verb+>+ [0] ;
    \item A[0] =  B[0] + Sum\verb+<+C, D\verb+>+ [0];
    \item A[0] = B[0] + (C[0] + D[0]);
    \item The above steps are repeated for A[1], and A[2].
  \end{enumerate}
\end{enumerate}

There are four possible ways in which a Sum object can represent
addition: Sum\verb+<+ T, T\verb+>+, Sum\verb+<+Vector, T\verb+>+, Sum\verb+<+T, Vector\verb+>+,
Sum\verb+<+Vector, Vector\verb+>+, where T represents a generic
template object.  Unfortunately Vector + Vector will match all of
the previous cases equally well.  The Expression class is essentially
a dummy class that ensures that the correct type of Sum object is used.

\section{benchmark Source Code}
\verb+benchmark+ is a program written to measure improvements in the
Irrlicht engine.  The source code comprises two files: \verb+main.cpp+ and \verb+game.h+
\subsection{main.cpp}\label{main}
\verb+main.cpp+ is the entry point of \verb+benchmark+.  In this file
command line arguments are handled.  Execution of the program is
achieved by calls to a \verb+Game+ object.
\begin{verbatim}
/*
  Collision Detection and Response Benchmark
  Ben Gilbert
  26th of February 2008
*/
#include <irrlicht.h>
#include <iostream>
#include <string>
#include "game.h"
using namespace irr;

#ifdef _MSC_VER
#pragma comment(lib, "Irrlicht.lib")
#endif

int main(int argc, char* argv[])
{
  bool useCollisionResponseAnimators = false;
  unsigned frameLimit = 0;
  if(argc != 3)
    {
      std::cout << "Incorrect argument format: benchmark [y or n] framelimit" 
                << std::endl;
      exit(1);
    }
  char yesStr[] = "y";
  char noStr[] = "n";
  if(strcmp(argv[1], yesStr) == 0)
    {
      std::cout << "here" << std::endl;
      useCollisionResponseAnimators = true;
    } 
  else
    {
      if(strcmp(argv[1], noStr) != 0)
	{
	    std::cout << "Incorrect argument format: benchmark [y or n] framelimit " 
                    << std::endl;
	    exit(1);
	} 
    }
  frameLimit = static_cast<unsigned>(atoi( argv[2]) );
  Game game;
  // the member functions of game must be called in this order
  game.createDevice();
  game.createVideoDriver();
  game.createSceneManager();
  game.loadQuakeMap();
  game.loadQuakeMesh();
  game.createQuakeSceneNode();
  game.createCamera();
  game.createTriangleSelector();
  game.createTarget();
  game.addLight();
  game.createBoxes();
  if(useCollisionResponseAnimators) 
    {
      game.addCollisionResponseAnimatorToBoxes();
    }
  return game.loop(frameLimit);
}


\end{verbatim}

\subsection{game.h}\label{game}
\verb+game.h+ is a header file for the \verb+Game+ class.
\begin{verbatim}
#ifndef __GAME_H__
#define __GAME_H__
#include <irrlicht.h>
#include <iostream>
#include <vector>

/*
  game.h - game object encapsulates the procedures of the main function
  Ben Gilbert
*/

const unsigned numberOfBoxes = 1000;
irr::u32 changeTime = 0;

class Box
{
 public:
  irr::scene::ISceneNode* sceneNode;
  irr::scene::ISceneManager* smgr;
  irr::core::vector3df direction;
	
 Box(irr::scene::ISceneManager* s) : 
  smgr(s), direction(irr::core::vector3df(1,1,1)) {}
  void createBox(	irr::video::IVideoDriver* driver) 
  {
    sceneNode = smgr->addCubeSceneNode();
    if (!sceneNode) {
      std::cout << "Error: Could not create Cube Scene Node.\n" << std::endl;
      exit(1); 
    }
    std::string sceneNodeTexture("../../media/t351sml.jpg");
    sceneNode->setMaterialTexture(0, driver->getTexture(sceneNodeTexture.c_str()));
    sceneNode->setMaterialFlag(irr::video::EMF_LIGHTING, false);
    const irr::core::vector3df default_sceneNode_position(
      irr::core::vector3df(0,0,0));
    sceneNode->setPosition(default_sceneNode_position);
  }
};

irr::core::vector3df randomVector() {
  irr::core::vector3df vec;
  vec.X = static_cast<irr::f32> (rand() % 200 - 100);
  vec.Y = static_cast<irr::f32> (rand() % 200 -100);
  vec.Z = static_cast<irr::f32> (rand() % 200 - 100);
  vec.normalize();
  return vec;
}

bool endGame = false;
irr::core::vector3df velocity(1, 1, 1);

class GameEventReceiver : public irr::IEventReceiver
{
 public:
  virtual bool OnEvent(const irr::SEvent& event)
  {
    if (event.EventType == irr::EET_KEY_INPUT_EVENT )
      {
	switch(event.KeyInput.Key)
	  {
	  case irr::KEY_ESCAPE:
	    {
	      endGame = true;
	      break;
	    }
	  default : {}
	  }

      }

    return false;
  }
};

class Game{
 public:

  Game() {}
  void createDevice() 
  {
    srand(0);

    const irr::core::dimension2d<irr::s32> screen_dimensions(640, 480);	
    const irr::video::E_DRIVER_TYPE device_type = irr::video::EDT_OPENGL;
    const irr::u32 bits_per_pixel = 16;
    const bool fullscreen = false;
    const bool stencil_buffer = false;
    const bool vertical_sync = false;
    gameEventReceiver = new GameEventReceiver();

    device = irr::createDevice(device_type, screen_dimensions, 
			       bits_per_pixel, fullscreen, stencil_buffer, 
			       vertical_sync, gameEventReceiver
			       );
    if (!device)
      {
	std::cout << "Error: Could not create Irrlicht device.\n" << std::endl;
	exit(1); 
      }
  }
  void createVideoDriver() 
  {
    driver = device->getVideoDriver(); 
    if(!device) 
      {
	std::cout << "Error: Could not create Video Driver.\n" << std::endl;
	exit(1);
      }
  }
  void createSceneManager()
  {
    smgr = device->getSceneManager();   
    if(!smgr) 
      {
	std::cout << "Error: Could not create Scene Manager.\n" << std::endl;
	exit(1);
      }
  }
  void loadQuakeMap()
  {
    const std::string quake_map_zip_file( "../../media/map-20kdm2.pk3");
    device->getFileSystem()->addZipFileArchive( quake_map_zip_file.c_str() );
  }
  void loadQuakeMesh()
  {
    const std::string quake_map_file( "20kdm2.bsp" );
    quake_mesh = smgr->getMesh( quake_map_file.c_str() ); 
    if (!quake_mesh)
      {
	std::cout << "Error: Could not load Quake Mesh.\n" << std::endl;
	exit(1);
      }
  }
  void createQuakeSceneNode() {
    quake_node = smgr->addOctTreeSceneNode(quake_mesh->getMesh(0));
    if(!quake_node) 
      {
	std::cout << "Error: Could not create Quake Octree Scene Node.\n" << std::endl;
	exit(1);
      }
    const irr::core::vector3df default_quake_node_position(-1350,-130,-1400);
    quake_node->setPosition(default_quake_node_position);
  }

  void createFPSCamera() 
  {
    irr::scene::ISceneNode* camera_parent = 0;
    const irr::f32 rotate_speed = 100.0f;
    const irr::f32 move_speed = 300.0f;
    const irr::s32 id = -1;
    irr::SKeyMap* key_map_array = 0;
    const irr::s32 key_map_size = 0;
    const bool no_vertical_movement = false;
    const irr::f32 jump_speed = 100.f;

    camera = smgr->addCameraSceneNodeFPS(
					 camera_parent, 
					 rotate_speed,
					 move_speed,
					 id,
					 key_map_array,
					 key_map_size,
					 no_vertical_movement,
					 jump_speed
					 );
    const irr::core::vector3df default_camera_position(-100,50,-150);
    camera->setPosition(default_camera_position);
  }  
  void createCamera() 
  {
   const irr::core::vector3df default_camera_position(-100,50,-150);
   irr::scene::ISceneNode* camera_parent = 0;
   const irr::core::vector3df lookat = irr::core::vector3df(0, 0, 100);
   const irr::s32 id = -1;
   camera = smgr->addCameraSceneNode(camera_parent, 
				     default_camera_position, 
				     lookat, 
				     id
				     ); 
  }
  void createTriangleSelector()
  {
    const irr::u32 min_polygons_octree_node = 128;
    selector = 0;
    selector = smgr->createOctTreeTriangleSelector(quake_mesh->getMesh(0), 
						   quake_node, min_polygons_octree_node);
    quake_node->setTriangleSelector(selector);
    if(!selector)
      {
	std::cout << "Error: Could not create Triangle Selector.\n" << std::endl;
	exit(1); 
      }
  }
  void addCollisionResponseAnimatorToCamera() 
  {

    const irr::core::vector3df ellipsoidRadius = irr::core::vector3df(30,50,30);
    const irr::core::vector3df gravityPerSecond = irr::core::vector3df(0,-3.0f,0);
    const irr::core::vector3df ellipsoidTranslation = irr::core::vector3df(0,50,0);
    irr::scene::ISceneNodeAnimator* anim = 
      smgr->createCollisionResponseAnimator (
					     selector, 
					     camera, 
					     ellipsoidRadius,
					     gravityPerSecond, 
					     ellipsoidTranslation
					     );
    camera->addAnimator(anim);
    anim->drop();
  }
  void createTarget() 
  {
    device->getCursorControl()->setVisible(false);
    const std::string target_bitmap( "../../media/particle.bmp");
    target = smgr->addBillboardSceneNode();
    target->setMaterialType(irr::video::EMT_TRANSPARENT_ADD_COLOR );
    target->setMaterialTexture(0, driver->getTexture( target_bitmap.c_str() ));
    target->setMaterialFlag(irr::video::EMF_LIGHTING, false);
    target->setMaterialFlag(irr::video::EMF_ZBUFFER, false);

    const irr::core::dimension2df target_size(20.0f, 20.0f);
    target->setSize(target_size);
  }
  void addLight() 
  {
    irr::scene::ISceneNode* light_parent = 0;
    const irr::core::vector3df light_position = irr::core::vector3df(0,0,0);
    const irr::video::SColorf light_color = irr::video::SColorf(1.0f, 1.0f, 1.0f);
    const irr::f32 light_radius = 100.0f; 
    const irr::s32 light_id = -1;
    smgr->addLightSceneNode(light_parent, light_position,
			    light_color,
			    light_radius, light_id);
  }

  int loop(unsigned frameLimit) 
  {
    int lastFPS = -1;
    unsigned frameCount = 0;

    while(device->run()  && !endGame ) 
      {
	if( frameLimit != 0)
	  {
	    if(frameCount >= frameLimit)
	      {
		break;
	      }
	  }
	if (device->isWindowActive())
	  {
	    driver->beginScene(true, true, 0);
	    updatePositions(!frameCount);
	    smgr->drawAll();
	    placeTarget();
	    driver->endScene();
	    lastFPS = updateCaption(lastFPS);
	    std::cout << lastFPS << std::endl;
	  }
	frameCount++;
      }
    device->drop();

    return 0;
  }
	
  void createBoxes() 
  {
    const irr::core::vector3df default_box_position(irr::core::vector3df(0,0,0));
    std::string boxTexture("../../media/t351sml.jpg");
    for(unsigned i = 0; i < numberOfBoxes; i++) {
      boxVector.push_back(new Box(smgr));
      boxVector[i]->createBox(driver);
      if (!boxVector[i]) {
	std::cout << "Error: Could not create Cube Scene Node.\n" << std::endl;
	exit(1); 
      }
    }

  }

  void addCollisionResponseAnimatorToBoxes() 
  {
    for(unsigned i = 0; i < numberOfBoxes; i++) 
      {
	const irr::core::aabbox3d<irr::f32>& boundingBox = 
         boxVector[i]->sceneNode->getBoundingBox();
	irr::core::vector3df ellipsoidRadius = 
         boundingBox.MaxEdge - boundingBox.getCenter();
	const irr::core::vector3df gravityPerSecond = irr::core::vector3df(0, 0.0f,0);
	irr::scene::ISceneNodeAnimator* anim = 
	  smgr->createCollisionResponseAnimator (
						 selector, 
						 boxVector[i]->sceneNode, 
						 ellipsoidRadius,
						 gravityPerSecond
						 );
	boxVector[i]->sceneNode->addAnimator(anim);
	anim->drop();

      }
  }
 private:
  irr::IrrlichtDevice* device;
  irr::video::IVideoDriver* driver;
  irr::scene::ISceneManager* smgr;
  irr::gui::IGUIEnvironment* guienv;
  irr::scene::IAnimatedMesh* quake_mesh;
  irr::scene::ISceneNode* quake_node;
  irr::scene::IBillboardSceneNode * target;
  irr::scene::ICameraSceneNode* camera;
  irr::scene::ITriangleSelector* selector;
  GameEventReceiver* gameEventReceiver;	
  std::vector<Box*> boxVector;


  void placeTarget() 
  {
    irr::core::line3d<irr::f32> line;
    line.start = camera->getPosition();
    line.end = line.start + (camera->getTarget() 
                          - line.start).normalize() * 1000.0f;

    irr::core::vector3df intersection;
    irr::core::triangle3df tri;
    if (smgr->getSceneCollisionManager()->getCollisionPoint(line, selector, 
							    intersection, tri))
      {
	target->setPosition(intersection);
	driver->setTransform(irr::video::ETS_WORLD, irr::core::matrix4());
      }
  }
  int updateCaption(int lastFPS) 
  {
    int fps = driver->getFPS();

    if (lastFPS != fps)
      {
	irr::core::stringw caption = L"Collision Detection and Response Benchmark [";
	caption += driver->getName();
	caption += "] FPS:";
	caption += fps;

	device->setWindowCaption(caption.c_str());
      }
    return fps; 

  }
  void updatePositions(bool isInitial)
  {

    if( isInitial)
      {
	for(unsigned i = 0; i < numberOfBoxes; i++)
	  {
	    velocity = randomVector() * 1.0;
	    boxVector[i]->direction = velocity;
				
	    irr::core::vector3df v = boxVector[i]->sceneNode->getPosition();
	    v += boxVector[i]->direction * 1.0;
	    boxVector[i]->sceneNode->setPosition(v);
	  }
      } else
      {
	for(unsigned i = 0; i < numberOfBoxes; i++)
	  {
	    irr::core::vector3df v = boxVector[i]->sceneNode->getPosition();
	    v += boxVector[i]->direction * 1.0;
	    boxVector[i]->sceneNode->setPosition(v);
	  }
      }
  }

};
#endif
\end{verbatim}

\section{Vector Source Code}
The source code in this section is designed to create two test
programs to compare the speed of a custom vector class using
expression templates against the Irrlicht vector class.

\subsection{vector\_optimised.h}\label{expression_vector}
\verb+vector_optimised.h+ defines the expression template
implementation of a vector class.
\begin{verbatim}
#ifndef __VECTOR_OPTIMIZED__
#define __VECTOR_OPTIMIZED__

#define VectorPlusVector Sum< Expression<Vector>, Expression<Vector> >
#define ExpressionPlusVector Sum< Expression<A>, Expression<Vector> >
#define VectorPlusExpression Sum< Expression<Vector>, Expression<B> >
#define ExpressionPlusExpression Sum< Expression<A>, Expression<B> >
#define ScalarTimesVector Product< Expression<Vector> >
#define ScalarTimesExpression Product< Expression<A> >

template < class T >
class Expression {
	const T expression;
public:
	Expression(const T& t) : expression(t) {}

	float operator[](int i) const
	{
		return expression[i];
	}
};

class Vector{
	float v[3];
public:
	Vector() {}
	Vector(float x_pos, float y_pos, float z_pos)
	{
		v[0] = x_pos;
		v[1] = y_pos;
		v[2] = z_pos;
	}
	float operator[](int i) const
	{
		return v[i];
	}
	float operator[](int i)
	{
		return v[i];
	}

	template <class T>
	Vector(const T& expression)
	{
		v[0] = expression[0];
		v[1] = expression[1];
		v[2] = expression[2];
	}

	template <class T>
	Vector& operator=(const T& expression)
	{
		v[0] = expression[0];
		v[1] = expression[1];
		v[2] = expression[2];
		return *this;
	}
};

template <>
class Expression<Vector> {
	const Vector& V;
public:
	Expression(const Vector& Vc) : V(Vc) {}
	float operator[] (int i) const
	{
		return V[i];
	}
};

template < class LeftT, class RightT >
class Sum {
	const LeftT L;
	const RightT R;
public:
	Sum(const LeftT Linit, const RightT Rinit) : L(Linit), R(Rinit) {}

	float operator[] (int i) const
	{
		return L[i] + R[i];
	}
};

template < class VectorExpressionT >
class Product {
	float scalar;
	const VectorExpressionT V;
public:
	Product( const float s, const VectorExpressionT Vinit) :
	  scalar(s), V(Vinit) {}

	  float operator[](int i) const
	  {
		  return scalar*V[i];
	  }
};

inline const 
Expression< VectorPlusVector > operator+( const Vector& a, const Vector& b)
{
	return Expression< VectorPlusVector>  ( VectorPlusVector(a,b) );
}

template< class A > inline const
Expression< ExpressionPlusVector > operator+( const Expression<A> &a, 
                                              const Vector& b)
{
	return Expression< ExpressionPlusVector > ( ExpressionPlusVector(a,b) );
}

template< class B > inline const
Expression< VectorPlusExpression > operator+(const Vector& a, 
                                             const Expression<B>& b)
{
	return Expression< VectorPlusExpression > ( VectorPlusExpression(a, b) );
}

template< class A, class B > inline const
Expression< ExpressionPlusExpression > operator+(const Expression<A>& a, 
                                                 const Expression<B>& b)
{
	return Expression< ExpressionPlusExpression >
         ( ExpressionPlusExpression(a, b) );
}

inline const
Expression< ScalarTimesVector> operator* (const  float s, const Vector& V)
{
	return Expression< ScalarTimesVector >
         ( ScalarTimesVector(s, V) );
}

template< class A> inline const
Expression< ScalarTimesExpression > operator* ( const float s, 
                                                const Expression<A> E)
{
	return Expression< ScalarTimesExpression > ( ScalarTimesExpression(s, E) );
}
#endif
\end{verbatim}


\subsection{vector\_test.cpp}\label{expression_vector_test}
\verb+vector_test.cpp+ is a program that evaluates vector expressions
using the expression template vector class.
\begin{verbatim}
#include  "vector_optimized.h"
#include <sys/time.h>
#include <iostream>

const long limit = 1000000;

void functionI() 
{
  Vector a(1.0, 1.0, 1.0);
  Vector b(10.0, 10.0, 10.0);
  Vector d(0.0, 0.0, 0.0);  
  for(long l = 0; l < limit - 1; l++)
    {
      d = a + b;
    }
}



void functionII() 
{
  Vector a(1.0, 1.0, 1.0);
  Vector b(10.0, 10.0, 10.0);
  Vector d(0.0, 0.0, 0.0);
  for(long l = 0; l < limit - 1; l++ )
    {
      d = 3 * a + 4 * b;
    }
}



void functionIII() 
{
  Vector a(1.0, 1.0, 1.0);
  Vector b(10.0, 10.0, 10.0);
  Vector c(100.0, 100.0, 100.0);
  Vector d(0.0, 0.0, 0.0);  
  for( long l = 0; l < limit - 1; l++ )
    {
      d = a + 3 * (b + c);
    }
}



void functionIV() 
{
  Vector a(1.0, 1.0, 1.0);
  Vector b(10.0, 10.0, 10.0);
  Vector c(100.0, 100.0, 100.0);
  Vector d(0.0, 0.0, 0.0);  
  for( long l = 0; l < limit - 1; l++ )
    {
      d = 3 * a + 4 * b + 5 * c;
    }
}


int main() 
{
  int timerMode = ITIMER_VIRTUAL;
  struct itimerval endCount;
  struct itimerval startCount;
  long timedif = 0;
  endCount.it_interval.tv_sec = 0;
  endCount.it_interval.tv_usec = 0;
  endCount.it_value.tv_sec = 1000000;
  endCount.it_value.tv_usec = 0;
  

   
  setitimer(timerMode, &endCount, NULL);
  getitimer(timerMode, &startCount);
  functionI();
  getitimer(timerMode, &endCount);
  timedif = 1000000 * (startCount.it_value.tv_sec - endCount.it_value.tv_sec) 
    + (startCount.it_value.tv_usec - endCount.it_value.tv_usec);
  std::cout << timedif << std::endl;
 

  
  setitimer(timerMode, &endCount, NULL);
  getitimer(timerMode, &startCount);
  functionII();
  getitimer(timerMode, &endCount);
  timedif = 1000000 * (startCount.it_value.tv_sec - endCount.it_value.tv_sec) 
    + (startCount.it_value.tv_usec - endCount.it_value.tv_usec);
  std::cout << timedif << std::endl;
 
 
   setitimer(timerMode, &endCount, NULL);
  getitimer(timerMode, &startCount);
  functionIII();
  getitimer(timerMode, &endCount);
  timedif = 1000000 * (startCount.it_value.tv_sec - endCount.it_value.tv_sec) 
    + (startCount.it_value.tv_usec - endCount.it_value.tv_usec);
  std::cout << timedif << std::endl;
  

  
  setitimer(timerMode, &endCount, NULL);
  getitimer(timerMode, &startCount);
  functionIV();
  getitimer(timerMode, &endCount);
  timedif = 1000000 * (startCount.it_value.tv_sec - endCount.it_value.tv_sec) 
    + (startCount.it_value.tv_usec - endCount.it_value.tv_usec);
  std::cout << timedif << std::endl;
  
 
  return 0;
}

\end{verbatim}


\subsection{vector\_test\_irr.cpp}\label{irr_vector_test}
\verb+vector_test_irr.cpp+ is a program that evaluates vector
expressions using Irrlicht's vector class.
\begin{verbatim}
#include "irrlicht.h"
#include <sys/time.h>
#include <iostream>

const long limit = 1000000;

void function1() 
{
  irr::core::vector3df a(1.0, 1.0, 1.0);
  irr::core::vector3df b(10.0, 10.0, 10.0);  
  irr::core::vector3df d(0.0, 0.0, 0.0);  

  for(long l = 0; l < limit - 1; l++)
    {
      d = a + b;
    }
  
}


void function2() 
{
  irr::core::vector3df a(1.0, 1.0, 1.0);
  irr::core::vector3df b(10.0, 10.0, 10.0);
  irr::core::vector3df d(0.0, 0.0, 0.0);
  for(long l = 0; l < limit - 1; l++ )
    {
      d = 3 * a + 4 * b;
    }
}



void function3() 
{
  irr::core::vector3df a(1.0, 1.0, 1.0);
  irr::core::vector3df b(10.0, 10.0, 10.0);
  irr::core::vector3df c(100.0, 100.0, 100.0);
  irr::core::vector3df d(0.0, 0.0, 0.0);  
  for( long l = 0; l < limit - 1; l++ )
    {
      d = a + 3 * (b + c);
    }
}

void function4() 
{
  irr::core::vector3df a(1.0, 1.0, 1.0);
  irr::core::vector3df b(10.0, 10.0, 10.0);
  irr::core::vector3df c(100.0, 100.0, 100.0);
  irr::core::vector3df d(0.0, 0.0, 0.0);  
  for( long l = 0; l < limit - 1; l++ )
    {
      d = 3 * a + 4 * b + 5 * c;
    }
}


int main() 
{
  int timerMode = ITIMER_VIRTUAL;
  struct itimerval endCount;
  struct itimerval startCount;
  long timedif = 0;
  endCount.it_interval.tv_sec = 0;
  endCount.it_interval.tv_usec = 0;
  endCount.it_value.tv_sec = 1000000;
  endCount.it_value.tv_usec = 0;
  

   
  setitimer(timerMode, &endCount, NULL);
  getitimer(timerMode, &startCount);
  function1();
  getitimer(timerMode, &endCount);
  timedif = 1000000 * (startCount.it_value.tv_sec - endCount.it_value.tv_sec) 
    + (startCount.it_value.tv_usec - endCount.it_value.tv_usec);
  std::cout << timedif << std::endl;
 

  
  setitimer(timerMode, &endCount, NULL);
  getitimer(timerMode, &startCount);
  function2();
  getitimer(timerMode, &endCount);
  timedif = 1000000 * (startCount.it_value.tv_sec - endCount.it_value.tv_sec) 
    + (startCount.it_value.tv_usec - endCount.it_value.tv_usec);
  std::cout << timedif << std::endl;
 
 
   setitimer(timerMode, &endCount, NULL);
  getitimer(timerMode, &startCount);
  function3();
  getitimer(timerMode, &endCount);
  timedif = 1000000 * (startCount.it_value.tv_sec - endCount.it_value.tv_sec) 
    + (startCount.it_value.tv_usec - endCount.it_value.tv_usec);
  std::cout << timedif << std::endl;
  

  
  setitimer(timerMode, &endCount, NULL);
  getitimer(timerMode, &startCount);
  function4();
  getitimer(timerMode, &endCount);
  timedif = 1000000 * (startCount.it_value.tv_sec - endCount.it_value.tv_sec) 
    + (startCount.it_value.tv_usec - endCount.it_value.tv_usec);
  std::cout << timedif << std::endl;
  
 
  return 0;
}

\end{verbatim}

\section{Assembly Code Files}\label{assembly_code}
\subsection{vector\_test.s}\label{expression_assembly}
\verb+vector_test.s+ is an assembly file produced from
\verb+vector_test.cpp+ when compiled using -O2 optimisation.
\begin{verbatim}
\subsection{Expression Template Implementation Assembly Code}
	.file	"vector_disassemble.cpp"
	.text
	.align 2
	.p2align 4,,15
.globl main
	.type	main, @function
main:
.LFB28:
	xorl	%eax, %eax
	ret
.LFE28:
	.size	main, .-main
.globl __gxx_personality_v0
	.section	.eh_frame,"a",@progbits
.Lframe1:
	.long	.LECIE1-.LSCIE1
.LSCIE1:
	.long	0x0
	.byte	0x1
	.string	"zPR"
	.uleb128 0x1
	.sleb128 -8
	.byte	0x10
	.uleb128 0x6
	.byte	0x3
	.long	__gxx_personality_v0
	.byte	0x3
	.byte	0xc
	.uleb128 0x7
	.uleb128 0x8
	.byte	0x90
	.uleb128 0x1
	.align 8
.LECIE1:
.LSFDE1:
	.long	.LEFDE1-.LASFDE1
.LASFDE1:
	.long	.LASFDE1-.Lframe1
	.long	.LFB28
	.long	.LFE28-.LFB28
	.uleb128 0x0
	.align 8
.LEFDE1:
	.ident	"GCC: (GNU) 4.1.3 20070929 (prerelease) (Ubuntu 4.1.2-16ubuntu2)"
	.section	.note.GNU-stack,"",@progbits
\end{verbatim}
\subsection{vector\_test\_irr.s}\label{irr_assembly}
\verb+vector_test_irr.s+ is an assembly file created when
\verb+vector_test_irr.cpp+ is compiled with -O3 optimisation.
\begin{verbatim}
	.file	"vector_disassemble_irr.cpp"
	.section	.ctors,"aw",@progbits
	.align 8
	.quad	_GLOBAL__I_main
	.section
      .text._ZN3irr4core12irrAllocatorIcE15internal_deleteEPv,"axG",
           @progbits,_ZN3irr4core12irrAllocatorIcE15internal_deleteEPv,comdat
	.align 2
	.p2align 4,,15
	.weak	_ZN3irr4core12irrAllocatorIcE15internal_deleteEPv
	.type	_ZN3irr4core12irrAllocatorIcE15internal_deleteEPv, @function
_ZN3irr4core12irrAllocatorIcE15internal_deleteEPv:
.LFB2135:
	movq	%rsi, %rdi
	jmp	_ZdlPv
.LFE2135:
	.size	_ZN3irr4core12irrAllocatorIcE15internal_deleteEPv, 
                    .-_ZN3irr4core12irrAllocatorIcE15internal_deleteEPv
.globl __gxx_personality_v0
	.section	.text._ZN3irr4core12irrAllocatorIcED0Ev,"axG",
                    @progbits,_ZN3irr4core12irrAllocatorIcED0Ev,comdat
	.align 2
	.p2align 4,,15
	.weak	_ZN3irr4core12irrAllocatorIcED0Ev
	.type	_ZN3irr4core12irrAllocatorIcED0Ev, @function
_ZN3irr4core12irrAllocatorIcED0Ev:
.LFB1933:
	movq	$_ZTVN3irr4core12irrAllocatorIcEE+16, (%rdi)
	jmp	_ZdlPv
.LFE1933:
	.size	_ZN3irr4core12irrAllocatorIcED0Ev, .-_ZN3irr4core12irrAllocatorIcED0Ev
	.section	.text._ZN3irr4core12irrAllocatorIcED1Ev,"axG",
                    @progbits,_ZN3irr4core12irrAllocatorIcED1Ev,comdat
	.align 2
	.p2align 4,,15
	.weak	_ZN3irr4core12irrAllocatorIcED1Ev
	.type	_ZN3irr4core12irrAllocatorIcED1Ev, @function
_ZN3irr4core12irrAllocatorIcED1Ev:
.LFB1932:
	movq	$_ZTVN3irr4core12irrAllocatorIcEE+16, (%rdi)
	ret
.LFE1932:
	.size	_ZN3irr4core12irrAllocatorIcED1Ev, .-_ZN3irr4core12irrAllocatorIcED1Ev
	.section
.text._ZN3irr4core12irrAllocatorIcE12internal_newEm,"axG",
                    @progbits,_ZN3irr4core12irrAllocatorIcE12internal_newEm,comdat
	.align 2
	.p2align 4,,15
	.weak	_ZN3irr4core12irrAllocatorIcE12internal_newEm
	.type	_ZN3irr4core12irrAllocatorIcE12internal_newEm, @function
_ZN3irr4core12irrAllocatorIcE12internal_newEm:
.LFB2136:
	movq	%rsi, %rdi
	jmp	_Znwm
.LFE2136:
	.size	_ZN3irr4core12irrAllocatorIcE12internal_newEm, 
                    .-_ZN3irr4core12irrAllocatorIcE12internal_newEm
	.text
	.align 2
	.p2align 4,,15
.globl main
	.type	main, @function
main:
.LFB1541:
	xorl	%eax, %eax
	ret
.LFE1541:
	.size	main, .-main
.globl _Unwind_Resume
	.align 2
	.p2align 4,,15
	.type	_Z41__static_initialization_and_destruction_0ii, @function
_Z41__static_initialization_and_destruction_0ii:
.LFB2180:
	subq	$8, %rsp
.LCFI0:
	subl	$1, %edi
	je	.L23
.L20:
	addq	$8, %rsp
	ret
	.p2align 4,,7
.L23:
	cmpl	$65535, %esi
	jne	.L20
	xorw	%si, %si
	movl	$_ZN3irr4core14IdentityMatrixE, %edi
	movl	$64, %edx
	movb	$0, _ZN3irr4core14IdentityMatrixE+64(%rip)
	call	memset
	movl	$0x3f800000, %eax
	movb	$1, _ZN3irr4core14IdentityMatrixE+64(%rip)
	movq	$0, _ZN3irr5scene6quake315irrEmptyStringcE(%rip)
	movl	%eax, _ZN3irr4core14IdentityMatrixE+60(%rip)
	movl	%eax, _ZN3irr4core14IdentityMatrixE+40(%rip)
	movl	$1, %esi
	movl	%eax, _ZN3irr4core14IdentityMatrixE+20(%rip)
	movl	%eax, _ZN3irr4core14IdentityMatrixE(%rip)
	movl	$_ZN3irr5scene6quake315irrEmptyStringcE+16, %edi
	movq	$_ZTVN3irr4core12irrAllocatorIcEE+16, 
                    _ZN3irr5scene6quake315irrEmptyStringcE+16(%rip)
	movl	$1, _ZN3irr5scene6quake315irrEmptyStringcE+12(%rip)
	movl	$1, _ZN3irr5scene6quake315irrEmptyStringcE+8(%rip)
.LEHB0:
	call	*_ZTVN3irr4core12irrAllocatorIcEE+32(%rip)
	movq	%rax, _ZN3irr5scene6quake315irrEmptyStringcE(%rip)
	xorl	%esi, %esi
	movb	$0, (%rax)
	movl	$_ZN3irr5scene6quake315irrEmptyStringcE+16, %edi
	movq	_ZN3irr5scene6quake315irrEmptyStringcE+16(%rip), %rax
	call	*24(%rax)
.LEHE0:
	movl	$__dso_handle, %edx
	xorl	%esi, %esi
	movl	$__tcf_0, %edi
	addq	$8, %rsp
	jmp	__cxa_atexit
.L21:
.L18:
	movq	%rax, %rdi
	movq	$_ZTVN3irr4core12irrAllocatorIcEE+16, 
                    _ZN3irr5scene6quake315irrEmptyStringcE+16(%rip)
.LEHB1:
	call	_Unwind_Resume
.LEHE1:
.LFE2180:
	.size	_Z41__static_initialization_and_destruction_0ii, 
                    .-_Z41__static_initialization_and_destruction_0ii
	.section	.gcc_except_table,"a",@progbits
.LLSDA2180:
	.byte	0xff
	.byte	0xff
	.byte	0x1
	.uleb128 .LLSDACSE2180-.LLSDACSB2180
.LLSDACSB2180:
	.uleb128 .LEHB0-.LFB2180
	.uleb128 .LEHE0-.LEHB0
	.uleb128 .L21-.LFB2180
	.uleb128 0x0
	.uleb128 .LEHB1-.LFB2180
	.uleb128 .LEHE1-.LEHB1
	.uleb128 0x0
	.uleb128 0x0
.LLSDACSE2180:
	.text
	.align 2
	.p2align 4,,15
	.type	_GLOBAL__I_main, @function
_GLOBAL__I_main:
.LFB2231:
	movl	$65535, %esi
	movl	$1, %edi
	jmp	_Z41__static_initialization_and_destruction_0ii
.LFE2231:
	.size	_GLOBAL__I_main, .-_GLOBAL__I_main
	.align 2
	.p2align 4,,15
	.type	__tcf_0, @function
__tcf_0:
.LFB2181:
	subq	$8, %rsp
.LCFI1:
	movl	$_ZN3irr5scene6quake315irrEmptyStringcE+16, %edi
	movq	_ZN3irr5scene6quake315irrEmptyStringcE(%rip), %rsi
	movq	_ZN3irr5scene6quake315irrEmptyStringcE+16(%rip), %rax
.LEHB2:
	call	*24(%rax)
.LEHE2:
	movq	$_ZTVN3irr4core12irrAllocatorIcEE+16, 
                    _ZN3irr5scene6quake315irrEmptyStringcE+16(%rip)
	addq	$8, %rsp
	ret
.L32:
.L28:
	movq	%rax, %rdi
	movq	$_ZTVN3irr4core12irrAllocatorIcEE+16, 
                    _ZN3irr5scene6quake315irrEmptyStringcE+16(%rip)
.LEHB3:
	call	_Unwind_Resume
.LEHE3:
.LFE2181:
	.size	__tcf_0, .-__tcf_0
	.section	.gcc_except_table
.LLSDA2181:
	.byte	0xff
	.byte	0xff
	.byte	0x1
	.uleb128 .LLSDACSE2181-.LLSDACSB2181
.LLSDACSB2181:
	.uleb128 .LEHB2-.LFB2181
	.uleb128 .LEHE2-.LEHB2
	.uleb128 .L32-.LFB2181
	.uleb128 0x0
	.uleb128 .LEHB3-.LFB2181
	.uleb128 .LEHE3-.LEHB3
	.uleb128 0x0
	.uleb128 0x0
.LLSDACSE2181:
	.text
	.local	_ZN3irr4core14IdentityMatrixE
	.comm	_ZN3irr4core14IdentityMatrixE,68,32
	.local	_ZN3irr5scene6quake315irrEmptyStringcE
	.comm	_ZN3irr5scene6quake315irrEmptyStringcE,24,16
	.weak	_ZTVN3irr4core12irrAllocatorIcEE
	.section	.rodata._ZTVN3irr4core12irrAllocatorIcEE,
                    "aG",@progbits,_ZTVN3irr4core12irrAllocatorIcEE,comdat
	.align 32
	.type	_ZTVN3irr4core12irrAllocatorIcEE, @object
	.size	_ZTVN3irr4core12irrAllocatorIcEE, 48
_ZTVN3irr4core12irrAllocatorIcEE:
	.quad	0
	.quad	_ZTIN3irr4core12irrAllocatorIcEE
	.quad	_ZN3irr4core12irrAllocatorIcED1Ev
	.quad	_ZN3irr4core12irrAllocatorIcED0Ev
	.quad	_ZN3irr4core12irrAllocatorIcE12internal_newEm
	.quad	_ZN3irr4core12irrAllocatorIcE15internal_deleteEPv
	.weak	_ZTIN3irr4core12irrAllocatorIcEE
	.section	.rodata._ZTIN3irr4core12irrAllocatorIcEE,"aG",
                    @progbits,_ZTIN3irr4core12irrAllocatorIcEE,comdat
	.align 16
	.type	_ZTIN3irr4core12irrAllocatorIcEE, @object
	.size	_ZTIN3irr4core12irrAllocatorIcEE, 16
_ZTIN3irr4core12irrAllocatorIcEE:
	.quad	_ZTVN10__cxxabiv117__class_type_infoE+16
	.quad	_ZTSN3irr4core12irrAllocatorIcEE
	.weak	_ZTSN3irr4core12irrAllocatorIcEE
	.section	.rodata._ZTSN3irr4core12irrAllocatorIcEE,"aG",
                    @progbits,_ZTSN3irr4core12irrAllocatorIcEE,comdat
	.align 16
	.type	_ZTSN3irr4core12irrAllocatorIcEE, @object
	.size	_ZTSN3irr4core12irrAllocatorIcEE, 29
_ZTSN3irr4core12irrAllocatorIcEE:
	.string	"N3irr4core12irrAllocatorIcEE"
	.section	.eh_frame,"a",@progbits
.Lframe1:
	.long	.LECIE1-.LSCIE1
.LSCIE1:
	.long	0x0
	.byte	0x1
	.string	"zPLR"
	.uleb128 0x1
	.sleb128 -8
	.byte	0x10
	.uleb128 0x7
	.byte	0x3
	.long	__gxx_personality_v0
	.byte	0x3
	.byte	0x3
	.byte	0xc
	.uleb128 0x7
	.uleb128 0x8
	.byte	0x90
	.uleb128 0x1
	.align 8
.LECIE1:
.LSFDE1:
	.long	.LEFDE1-.LASFDE1
.LASFDE1:
	.long	.LASFDE1-.Lframe1
	.long	.LFB2135
	.long	.LFE2135-.LFB2135
	.uleb128 0x4
	.long	0x0
	.align 8
.LEFDE1:
.LSFDE3:
	.long	.LEFDE3-.LASFDE3
.LASFDE3:
	.long	.LASFDE3-.Lframe1
	.long	.LFB1933
	.long	.LFE1933-.LFB1933
	.uleb128 0x4
	.long	0x0
	.align 8
.LEFDE3:
.LSFDE5:
	.long	.LEFDE5-.LASFDE5
.LASFDE5:
	.long	.LASFDE5-.Lframe1
	.long	.LFB1932
	.long	.LFE1932-.LFB1932
	.uleb128 0x4
	.long	0x0
	.align 8
.LEFDE5:
.LSFDE7:
	.long	.LEFDE7-.LASFDE7
.LASFDE7:
	.long	.LASFDE7-.Lframe1
	.long	.LFB2136
	.long	.LFE2136-.LFB2136
	.uleb128 0x4
	.long	0x0
	.align 8
.LEFDE7:
.LSFDE9:
	.long	.LEFDE9-.LASFDE9
.LASFDE9:
	.long	.LASFDE9-.Lframe1
	.long	.LFB1541
	.long	.LFE1541-.LFB1541
	.uleb128 0x4
	.long	0x0
	.align 8
.LEFDE9:
.LSFDE11:
	.long	.LEFDE11-.LASFDE11
.LASFDE11:
	.long	.LASFDE11-.Lframe1
	.long	.LFB2180
	.long	.LFE2180-.LFB2180
	.uleb128 0x4
	.long	.LLSDA2180
	.byte	0x4
	.long	.LCFI0-.LFB2180
	.byte	0xe
	.uleb128 0x10
	.align 8
.LEFDE11:
.LSFDE13:
	.long	.LEFDE13-.LASFDE13
.LASFDE13:
	.long	.LASFDE13-.Lframe1
	.long	.LFB2231
	.long	.LFE2231-.LFB2231
	.uleb128 0x4
	.long	0x0
	.align 8
.LEFDE13:
.LSFDE15:
	.long	.LEFDE15-.LASFDE15
.LASFDE15:
	.long	.LASFDE15-.Lframe1
	.long	.LFB2181
	.long	.LFE2181-.LFB2181
	.uleb128 0x4
	.long	.LLSDA2181
	.byte	0x4
	.long	.LCFI1-.LFB2181
	.byte	0xe
	.uleb128 0x10
	.align 8
.LEFDE15:
	.ident	"GCC: (GNU) 4.1.3 20070929 (prerelease) (Ubuntu 4.1.2-16ubuntu2)"
	.section	.note.GNU-stack,"",@progbits

\end{verbatim}	
\end{document}
